package com.config.channels.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;


/**
 * 支持HMAC-SHA1消息签名 及 DES/AES对称加密的工具类.
 * 
 * 支持Hex与Base64两种编码方式.
 * 
 */
public class Cryptos {
	/**
	 * 自定义密钥
	 */
	//电视直播key
//	private String strDefaultKey = "!$@yunbokeji0501";
	//电视直播代理加密key
	private String strDefaultKey = "!@#ashd0303#$!@6";
	private byte[] key = strDefaultKey.getBytes();

	//随机矢量的定义
	//private byte[] ivByte = {0x41, 0x72, 0x65, 0x79, 0x6F, 0x75, 0x7C, 0x3E,0x7F,0x6E, 0x36, 0x26, 0x0D, 0x61, 0x6E, 0x3F};
	private byte[] ivByte = {0x41, 0x72, 0x6F, 0x79, 0x61, 0x75, 0x7C, 0x3E,0x36,0x6E, 0x36, 0x26, 0x0D, 0x61, 0x6E, 0x3F};
	
	private static final String TAG = "Cryptos";
	private static final String AES = "AES";
	private static final String AES_CBC = "AES/CBC/PKCS5Padding";
	private static final String HMACSHA1 = "HmacSHA1";

	private static final int DEFAULT_HMACSHA1_KEYSIZE = 160; //RFC2401
	private static final int DEFAULT_AES_KEYSIZE = 128;
	private static final int DEFAULT_IVSIZE = 16;

	private static SecureRandom random = new SecureRandom();

	private Logger logger = LoggerFactory.getLogger(this.getClass());

	public Cryptos(){
		
	}
	public Cryptos(String strDefaultKey){
		this.strDefaultKey = strDefaultKey;
		this.key = strDefaultKey.getBytes();
	}

	//-- HMAC-SHA1 funciton --//
	/**
	 * 使用HMAC-SHA1进行消息签名, 返回字节数组,长度为20字节.
	 * 
	 * @param input 原始输入字符数组
	 * @param key HMAC-SHA1密钥
	 */
	public static byte[] hmacSha1(byte[] input, byte[] key) {
		try {
			SecretKey secretKey = new SecretKeySpec(key, HMACSHA1);
			Mac mac = Mac.getInstance(HMACSHA1);
			mac.init(secretKey);
			return mac.doFinal(input);
		} catch (GeneralSecurityException e) {
			throw new CryptException("使用HMAC-SHA1进行消息签名出错：",e);
		}
	}

	/**
	 * 校验HMAC-SHA1签名是否正确.
	 *
	 * @param expected 已存在的签名
	 * @param input 原始输入字符串
	 * @param key 密钥
	 */
	public static boolean isMacValid(byte[] expected, byte[] input, byte[] key) {
		byte[] actual = hmacSha1(input, key);
		return Arrays.equals(expected, actual);
	}

	/**
	 * 生成HMAC-SHA1密钥,返回字节数组,长度为160位(20字节).
	 * HMAC-SHA1算法对密钥无特殊要求, RFC2401建议最少长度为160位(20字节).
	 */
	public static byte[] generateHmacSha1Key() {
		try {
			KeyGenerator keyGenerator = KeyGenerator.getInstance(HMACSHA1);
			keyGenerator.init(DEFAULT_HMACSHA1_KEYSIZE);
			SecretKey secretKey = keyGenerator.generateKey();
			return secretKey.getEncoded();
		} catch (GeneralSecurityException e) {
			throw new CryptException("生成HMAC-SHA1密钥出错：",e);
		}
	}

	//-- AES funciton --//
	/**
	 * 使用AES加密原始字符串.
	 * @param input 原始输入字符数组
	 *
	 */
	//@param key 符合AES要求的密钥
	public byte[] aesEncrypt(byte[] input) {
		if (key.length != 16) {
			logger.info("Key长度不是16位");
		}
		
		return aes(input, key, ivByte , Cipher.ENCRYPT_MODE);
	}

	/**
	 * 使用AES加密原始字符串.
	 *
	 * @param input 原始输入字符串
	 * @throws UnsupportedEncodingException
	 */
	public String aesEncrypt(String input) {
		byte[] aesEncode = aes(input.getBytes(), key, ivByte , Cipher.ENCRYPT_MODE);
		String aesEncodeStr = Base64.getUrlEncoder().encodeToString(aesEncode);
		return aesEncodeStr;
	}

	/**
	 * 使用AES加密原始字符串.
	 *
	 * @param input 原始输入字符数组
	 * @param key 符合AES要求的密钥
	 * @param iv 初始向量
	 */
	public static byte[] aesEncrypt(byte[] input, byte[] key, byte[] iv) {
		return aes(input, key, iv, Cipher.ENCRYPT_MODE);
	}

	/**
	 * 使用AES解密字符串, 返回原始字符串.
	 *
	 * @param input Hex编码的加密字符串
	 *
	 */
	//@param key 符合AES要求的密钥
	public String aesDecrypt(byte[] input){
		if (key.length != 16) {
			logger.info("Key长度不是16位");
		}
		byte[] decryptResult = aes(input, key, ivByte , Cipher.DECRYPT_MODE);
		return new String(decryptResult);
	}

	/**
	 * 使用AES解密字符串, 返回原始字符串.
	 *
	 * @param input Hex编码的加密字符串
	 * @throws UnsupportedEncodingException
	 */
	public String aesDecrypt(String input) {
		byte[] str_base64decode = Base64.getUrlDecoder().decode(input.getBytes());
		
		try{
			return aesDecrypt(str_base64decode);
		} catch (Exception e) {
			throw new CryptException("解密出错：",e);
		}
	}

	/**
	 * 使用AES解密字符串, 返回原始字符串.
	 *
	 * @param input Hex编码的加密字符串
	 * @param key 符合AES要求的密钥
	 * @param iv 初始向量
	 * @throws UnsupportedEncodingException
	 */
	public static String aesDecrypt(byte[] input, byte[] key, byte[] iv) {
		byte[] decryptResult = aes(input, key, iv, Cipher.DECRYPT_MODE);
		return new String(decryptResult);
	}

	/**
	 * 使用AES加密或解密无编码的原始字节数组, 返回无编码的字节数组结果.
	 *
	 * @param input 原始字节数组
	 * @param key 符合AES要求的密钥
	 * @param mode Cipher.ENCRYPT_MODE 或 Cipher.DECRYPT_MODE
	 */
	private static byte[] aes(byte[] input, byte[] key, int mode) {
		try {
			SecretKey secretKey = new SecretKeySpec(key, AES);
			Cipher cipher = Cipher.getInstance(AES);
			cipher.init(mode, secretKey);
			return cipher.doFinal(input);
		} catch (GeneralSecurityException e) {
			throw new CryptException("使用AES加密或解密无编码的原始字节数组出错：",e);
		}
	}

	/**
	 * 使用AES加密或解密无编码的原始字节数组, 返回无编码的字节数组结果.
	 *
	 * @param input 原始字节数组
	 * @param key 符合AES要求的密钥
	 * @param iv 初始向量
	 * @param mode Cipher.ENCRYPT_MODE 或 Cipher.DECRYPT_MODE
	 */
	private static byte[] aes(byte[] input, byte[] key, byte[] iv, int mode) {
		try {
			SecretKey secretKey = new SecretKeySpec(key, AES);
			IvParameterSpec ivSpec = new IvParameterSpec(iv);
			Cipher cipher = Cipher.getInstance(AES_CBC);
			cipher.init(mode, secretKey, ivSpec);
			return cipher.doFinal(input);
		} catch (GeneralSecurityException e) {
			throw new CryptException("使用AES加密或解密无编码的原始字节数组：",e);
		}
	}

	/**
	 * 生成AES密钥,返回字节数组, 默认长度为128位(16字节).
	 */
	public static byte[] generateAesKey() {
		return generateAesKey(DEFAULT_AES_KEYSIZE);
	}

	/**
	 * 生成AES密钥,可选长度为128,192,256位.
	 */
	public static byte[] generateAesKey(int keysize) {
		try {
			KeyGenerator keyGenerator = KeyGenerator.getInstance(AES);
			keyGenerator.init(keysize);
			SecretKey secretKey = keyGenerator.generateKey();
			return secretKey.getEncoded();
		} catch (GeneralSecurityException e) {
			throw new CryptException("生成AES密钥出错：",e);
		}
	}
	
	public byte[] getKey() {
		return key;
	}
	public void setKey(byte[] key) {
		this.key = key;
	}
}